/*
 * ******************************************************************************
 *   Copyright (c) 2013-2014 Gabriele Mariotti.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *  *****************************************************************************
 */

package it.gmariotti.cardslib.library.view;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.LinearLayout;

import java.util.HashMap;

import it.gmariotti.cardslib.library.R;
import it.gmariotti.cardslib.library.internal.Card;
import it.gmariotti.cardslib.library.internal.CardExpand;
import it.gmariotti.cardslib.library.internal.CardHeader;
import it.gmariotti.cardslib.library.internal.CardThumbnail;
import it.gmariotti.cardslib.library.internal.ViewToClickToExpand;
import it.gmariotti.cardslib.library.view.base.CardViewWrapper;
import it.gmariotti.cardslib.library.view.component.CardHeaderView;
import it.gmariotti.cardslib.library.view.component.CardThumbnailView;
import it.gmariotti.cardslib.library.view.listener.SwipeDismissViewTouchListener;

/**
 * Card view </p> Use an XML layout file to display it. </p> First, you need an
 * XML layout that will display the Card.
 * 
 * <pre>
 * <code>
 *  <it.gmariotti.cardslib.library.view.CardView
 *     android:id="@+id/carddemo_example_card3"
 *     android:layout_width="match_parent"
 *     android:layout_height="wrap_content"
 *     android:layout_marginLeft="12dp"
 *     android:layout_marginRight="12dp"
 *     android:layout_marginTop="12dp"/>
 * </code>
 * </pre>
 * 
 * Then create a model:
 * 
 * <pre>
 * <code>
 * 
 *     //Create a Card
 *     Card card = new Card(getContext());
 * 
 *     //Create a CardHeader
 *     CardHeader header = new CardHeader(getContext());
 * 
 *     //Add Header to card
 *     card.addCardHeader(header);
 * 
 * </code>
 * </pre>
 * 
 * Last get a reference to the `CardView` from your code, and set your `Card.
 * 
 * <pre>
 * <code>
 *     //Set card in the cardView
 *     CardView cardView = (CardView) getActivity().findViewById(R.id.carddemo);
 * 
 *     cardView.setCard(card);
 * </code>
 * </pre>
 * 
 * You can easily build your layout. </p> The quickest way to start with this
 * would be to copy one of this files and create your layout. Then you can
 * inflate your layout in the `CardView` using the attr:
 * `card:card_layout_resourceID="@layout/my_layout` Example:
 * 
 * <pre>
 * <code>
 *      <it.gmariotti.cardslib.library.view.CardView
 *       android:id="@+id/carddemo_thumb_url"
 *        android:layout_width="match_parent"
 *        android:layout_height="wrap_content"
 *        android:layout_marginLeft="12dp"
 *        android:layout_marginRight="12dp"
 *        card:card_layout_resourceID="@layout/card_thumbnail_layout"
 *        android:layout_marginTop="12dp"/>
 * </code>
 * </pre>
 * 
 * </p>
 * 
 * @author Gabriele Mariotti (gabri.mariotti@gmail.com)
 */
public class CardView extends BaseCardView implements CardViewWrapper {

	// --------------------------------------------------------------------------
	//
	// --------------------------------------------------------------------------

	/**
	 * {@link CardHeader} model
	 */
	protected CardHeader mCardHeader;

	/**
	 * {@link CardThumbnail} model
	 */
	protected CardThumbnail mCardThumbnail;

	/**
	 * {@link CardExpand} model
	 */
	protected CardExpand mCardExpand;

	// --------------------------------------------------------------------------
	// Layout
	// --------------------------------------------------------------------------

	/**
	 * Main Layout
	 */
	protected View mInternalMainCardLayout;

	/**
	 * Content Layout
	 */
	protected View mInternalContentLayout;

	/**
	 * Inner View.
	 */
	protected View mInternalInnerView;

	/**
	 * Hidden layout used by expand/collapse action
	 */
	protected View mInternalExpandLayout;

	/**
	 * Expand Inner view
	 */
	protected View mInternalExpandInnerView;

	/** Animator to expand/collapse */
	protected Animator mExpandAnimator;

	/**
	 * Listener invoked when Expand Animator starts It is used internally
	 */
	protected OnExpandListAnimatorListener mOnExpandListAnimatorListener;

	// --------------------------------------------------------------------------
	// Constructor
	// --------------------------------------------------------------------------

	public CardView(Context context) {
		super(context);
	}

	public CardView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public CardView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	// --------------------------------------------------------------------------
	// Init
	// --------------------------------------------------------------------------

	/**
	 * Init custom attrs.
	 *
	 * @param attrs
	 * @param defStyle
	 */
	protected void initAttrs(AttributeSet attrs, int defStyle) {

		card_layout_resourceID = R.layout.card_layout;

		TypedArray a = getContext().getTheme().obtainStyledAttributes(attrs,
				R.styleable.card_options, defStyle, defStyle);

		try {
			card_layout_resourceID = a.getResourceId(
					R.styleable.card_options_card_layout_resourceID, this.card_layout_resourceID);
		} finally {
			a.recycle();
		}
	}

	@TargetApi(Build.VERSION_CODES.LOLLIPOP)
	@Override
	public void drawableHotspotChanged(float x, float y) {
		super.drawableHotspotChanged(x, y);
		if (mInternalMainCardLayout != null
				&& mInternalMainCardLayout instanceof ForegroundLinearLayout) {
			mInternalMainCardLayout.drawableHotspotChanged(x, y);
		}
	}

	// --------------------------------------------------------------------------
	// Card
	// --------------------------------------------------------------------------

	/**
	 * Add a {@link Card}. It is very important to set all values and all
	 * components before launch this method.
	 *
	 * @param card
	 *            {@link Card} model
	 */
	@Override
	public void setCard(Card card) {

		super.setCard(card);
		if (card != null) {
			mCardHeader = card.getCardHeader();
			mCardThumbnail = card.getCardThumbnail();
			mCardExpand = card.getCardExpand();
		}

		// Retrieve all IDs
		if (!isRecycle()) {
			retrieveLayoutIDs();
		}

		// Build UI
		buildUI();
	//add by toby
		if (mInternalMainCardLayout != null) {
			LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mInternalMainCardLayout
					.getLayoutParams();
			if(card.getMarginTop() != -1){
				lp.setMargins(lp.leftMargin, card.getMarginTop(), lp.rightMargin, lp.bottomMargin);
				mInternalMainCardLayout.setLayoutParams(lp);
			}
			
			if(card.getMarginBottom() != -1){
				lp.setMargins(lp.leftMargin, lp.topMargin, lp.rightMargin, card.getMarginBottom());
				mInternalMainCardLayout.setLayoutParams(lp);
			}
		}
	}

	/**
	 * Refreshes the card content (it doesn't inflate layouts again)
	 *
	 * @param card
	 */
	public void refreshCard(Card card) {
		mIsRecycle = true;
		setCard(card);
		mIsRecycle = false;
	}

	/**
	 * Refreshes the card content and replaces the inner layout elements (it
	 * inflates layouts again!)
	 *
	 * @param card
	 */
	public void replaceCard(Card card) {
		mForceReplaceInnerLayout = true;
		refreshCard(card);
		mForceReplaceInnerLayout = false;
	}

	// --------------------------------------------------------------------------
	// Setup methods
	// --------------------------------------------------------------------------

	@Override
	protected void buildUI() {

		super.buildUI();

		mCard.setCardView(this);

		// Setup Header view
		setupHeaderView();

		// Setup Main View
		setupMainView();

		// setup Thumbnail
		setupThumbnailView();

		// Setup Expand View
		setupExpandView();

		// Setup Supplemental Actions
		setupSupplementalActions();

		// Setup Listeners
		setupListeners();

		// Setup Expand Action
		setupExpandAction();

		// Setup Drawable Resources
		setupDrawableResources();
	}

	/**
	 * Retrieve all Layouts IDs
	 */
	@Override
	protected void retrieveLayoutIDs() {

		super.retrieveLayoutIDs();

		// Main Layout
		mInternalMainCardLayout = findViewById(R.id.card_main_layout);

		// Get HeaderLayout
		mInternalHeaderLayout = findViewById(R.id.card_header_layout);

		// Get ExpandHiddenView
		mInternalExpandLayout = findViewById(R.id.card_content_expand_layout);

		// Get ContentLayout
		mInternalContentLayout = findViewById(R.id.card_main_content_layout);

		// Get ThumbnailLayout
		mInternalThumbnailLayout = findViewById(R.id.card_thumbnail_layout);
	}

	/**
	 * Setup Header View
	 */
	protected void setupHeaderView() {

		if (mCardHeader != null) {

			if (mInternalHeaderLayout != null) {
				mInternalHeaderLayout.setVisibility(VISIBLE);

				// Set recycle value (very important in a ListView)
				mInternalHeaderLayout.setRecycle(isRecycle());
				mInternalHeaderLayout.setForceReplaceInnerLayout(isForceReplaceInnerLayout());
				// Add Header View
				mInternalHeaderLayout.addCardHeader(mCardHeader);

			}
		} else {
			// No header. Hide layouts
			if (mInternalHeaderLayout != null) {
				mInternalHeaderLayout.setVisibility(GONE);
				// mInternalExpandLayout.setVisibility(View.GONE);

				if (isForceReplaceInnerLayout()) {
					mInternalHeaderLayout.addCardHeader(null);
					// mInternalHeaderLayout.removeAllViews();
				}
			}
		}
	}

	/**
	 * Setup the Main View
	 */
	protected void setupMainView() {
		if (mInternalContentLayout != null) {

			ViewGroup mParentGroup = null;
			try {
				mParentGroup = (ViewGroup) mInternalContentLayout;
			} catch (Exception e) {
				setRecycle(false);
			}

			// Check if view can be recycled
			// It can happen in a listView, and improves performances
			if (!isRecycle() || isForceReplaceInnerLayout()) {

				if (isForceReplaceInnerLayout() && mInternalContentLayout != null
						&& mInternalInnerView != null)
					((ViewGroup) mInternalContentLayout).removeView(mInternalInnerView);

				mInternalInnerView = mCard.getInnerView(getContext(),
						(ViewGroup) mInternalContentLayout);
			} else {
				// View can be recycled.
				// Only setup Inner Elements
				if (mCard.getInnerLayout() > -1)
					mCard.setupInnerViewElements(mParentGroup, mInternalInnerView);
			}
		}
	}

	/**
	 * Setup the Thumbnail View
	 */
	protected void setupThumbnailView() {
		if (mInternalThumbnailLayout != null) {
			if (mCardThumbnail != null) {
				mInternalThumbnailLayout.setVisibility(VISIBLE);
				mInternalThumbnailLayout.setRecycle(isRecycle());
				mInternalThumbnailLayout.setForceReplaceInnerLayout(isForceReplaceInnerLayout());
				mInternalThumbnailLayout.addCardThumbnail(mCardThumbnail);
			} else {
				mInternalThumbnailLayout.setVisibility(GONE);
			}
		}
	}

	/**
	 * Setup Drawable Resources
	 */
	protected void setupDrawableResources() {

		// Card
		if (mCard != null) {
			if (mCard.getBackgroundResourceId() != 0) {
				changeBackgroundResourceId(mCard.getBackgroundResourceId());
			} else if (mCard.getBackgroundResource() != null) {
				changeBackgroundResource(mCard.getBackgroundResource());
			}
		}
	}

	protected void setupSupplementalActions() {
		if (mCard != null)
			mCard.setupSupplementalActions();
	}

	// --------------------------------------------------------------------------
	// Listeners
	// --------------------------------------------------------------------------

	protected void setupExpandAction() {

		// Config ExpandLayout and its animation
		if (mInternalExpandLayout != null
				&& ((mCardHeader != null && mCardHeader.isButtonExpandVisible()) || mCard
						.getViewToClickToExpand() != null)) {

			// Create the expand/collapse animator
			mInternalExpandLayout.getViewTreeObserver().addOnPreDrawListener(
					new ViewTreeObserver.OnPreDrawListener() {

						@Override
						public boolean onPreDraw() {
							mInternalExpandLayout.getViewTreeObserver().removeOnPreDrawListener(
									this);

							View parent = (View) mInternalExpandLayout.getParent();
							final int widthSpec = View.MeasureSpec.makeMeasureSpec(
									parent.getMeasuredWidth() - parent.getPaddingLeft()
											- parent.getPaddingRight(), View.MeasureSpec.AT_MOST);
							final int heightSpec = View.MeasureSpec.makeMeasureSpec(0,
									View.MeasureSpec.UNSPECIFIED);
							mInternalExpandLayout.measure(widthSpec, heightSpec);

							mExpandAnimator = ExpandCollapseHelper.createSlideAnimator(
									(CardView) mCard.getCardView(), 0,
									mInternalExpandLayout.getMeasuredHeight());
							return true;
						}
					});
		}

		// Setup action and callback
		setupExpandCollapseActionListener();
	}

	/**
	 * Setup All listeners
	 */
	@SuppressWarnings("deprecation")
	@SuppressLint("NewApi")
	protected void setupListeners() {

		// Swipe listener
		if (mCard.isSwipeable()) {
			this.setOnTouchListener(new SwipeDismissViewTouchListener(this, mCard,
					new SwipeDismissViewTouchListener.DismissCallbacks() {
						@Override
						public boolean canDismiss(Card card) {
							return card.isSwipeable();
						}

						@Override
						public void onDismiss(CardViewWrapper cardView, Card card) {
							final ViewGroup vg = (ViewGroup) (((View) cardView).getParent());
							if (vg != null) {
								vg.removeView((View) cardView);
								card.onSwipeCard();
							}
						}
					}));
		} else {
			this.setOnTouchListener(null);
		}

		// OnClick listeners and partial listener

		// Reset Partial Listeners
		resetPartialListeners();

		if (mCard.isClickable()) {
			// Set the onClickListener
			if (!mCard.isMultiChoiceEnabled()) {
				if (mCard.getOnClickListener() != null) {
					this.setOnClickListener(new OnClickListener() {
						@Override
						public void onClick(View v) {
							if (mCard.getOnClickListener() != null)
								mCard.getOnClickListener().onClick(mCard, v);
						}
					});

					// Prevent multiple events
					// if (!mCard.isSwipeable() && mCard.getOnSwipeListener() ==
					// null) {
					// this.setClickable(true);
					// }

				} else {
					HashMap<Integer, Card.OnCardClickListener> mMultipleOnClickListner = mCard
							.getMultipleOnClickListener();
					if (mMultipleOnClickListner != null && !mMultipleOnClickListner.isEmpty()) {

						for (int key : mMultipleOnClickListner.keySet()) {
							View viewClickable = decodeAreaOnClickListener(key);
							final Card.OnCardClickListener mListener = mMultipleOnClickListner
									.get(key);
							if (viewClickable != null) {
								// Add listener to this view
								viewClickable.setOnClickListener(new OnClickListener() {
									@Override
									public void onClick(View v) {
										// Callback to card listener
										if (mListener != null)
											mListener.onClick(mCard, v);
									}
								});

								// Add Selector to this view
								if (key > Card.CLICK_LISTENER_ALL_VIEW) {
									mHelperImpl.setCardSelector(viewClickable, getResources()
											.getDrawable(R.drawable.card_selector));

								}
							}
						}
					} else {
						// There aren't listners
						this.setClickable(false);
					}
				}
			}
		} else {
			this.setClickable(false);
		}

		// LongClick listener
		if (mCard.isLongClickable()) {
			this.setOnLongClickListener(new OnLongClickListener() {
				@Override
				public boolean onLongClick(View v) {
					if (mCard.getOnLongClickListener() != null)
						return mCard.getOnLongClickListener().onLongClick(mCard, v);
					return false;
				}
			});
		} else {
			this.setLongClickable(false);
		}
	}

	/**
	 * Reset all partial listeners
	 */
	protected void resetPartialListeners() {
		View viewClickable = decodeAreaOnClickListener(Card.CLICK_LISTENER_HEADER_VIEW);
		if (viewClickable != null)
			viewClickable.setClickable(false);

		viewClickable = decodeAreaOnClickListener(Card.CLICK_LISTENER_THUMBNAIL_VIEW);
		if (viewClickable != null)
			viewClickable.setClickable(false);

		viewClickable = decodeAreaOnClickListener(Card.CLICK_LISTENER_CONTENT_VIEW);
		if (viewClickable != null)
			viewClickable.setClickable(false);
	}

	/**
	 *
	 * @param area
	 * @return
	 */
	protected View decodeAreaOnClickListener(int area) {

		if (area < Card.CLICK_LISTENER_ALL_VIEW && area > Card.CLICK_LISTENER_CONTENT_VIEW)
			return null;

		View view = null;

		switch (area) {
		case Card.CLICK_LISTENER_ALL_VIEW:
			view = this;
			break;
		case Card.CLICK_LISTENER_HEADER_VIEW:
			view = mInternalHeaderLayout;
			break;
		case Card.CLICK_LISTENER_THUMBNAIL_VIEW:
			view = mInternalThumbnailLayout;
			break;
		case Card.CLICK_LISTENER_CONTENT_VIEW:
			view = mInternalContentLayout;
			break;
		default:
			break;
		}
		return view;
	}

	// --------------------------------------------------------------------------
	// Expandable Actions and Listeners
	// --------------------------------------------------------------------------

	/**
	 * Add ClickListener to expand and collapse hidden view
	 */
	protected void setupExpandCollapseActionListener() {
		if (mInternalExpandLayout != null) {
			mInternalExpandLayout.setVisibility(View.GONE);

			boolean internal_blockForLongClickOnImageButtonExpand = false;
			ViewToClickToExpand viewToClickToExpand = null;

			// ButtonExpandVisible has a priority to viewClickToExpand
			if (mCardHeader != null && mCardHeader.isButtonExpandVisible()) {

				viewToClickToExpand = ViewToClickToExpand.builder()
						.setupView(mInternalHeaderLayout.getImageButtonExpand())
						.highlightView(true);
				internal_blockForLongClickOnImageButtonExpand = true;

			} else if (mCard.getViewToClickToExpand() != null) {

				viewToClickToExpand = mCard.getViewToClickToExpand();
			}

			if (viewToClickToExpand != null) {

				TitleViewOnClickListener titleViewOnClickListener = new TitleViewOnClickListener(
						mInternalExpandLayout, mCard, viewToClickToExpand.isViewToSelect());

				/*
				 * if (mCardHeader!=null && mCardHeader.isButtonExpandVisible()
				 * && mInternalHeaderLayout != null) {
				 * mInternalHeaderLayout.setOnClickExpandCollapseActionListener
				 * (titleViewOnClickListener); }
				 */

				View viewToClick = viewToClickToExpand.getViewToClick();
				if (viewToClick != null) {

					if (internal_blockForLongClickOnImageButtonExpand) {
						// The long click on Header button is now allowed
						viewToClick.setOnClickListener(titleViewOnClickListener);
					} else {
						if (viewToClickToExpand.isUseLongClick()) {
							viewToClick.setOnLongClickListener(new TitleViewOnLongClickListener(
									titleViewOnClickListener));
						} else {
							viewToClick.setOnClickListener(titleViewOnClickListener);
						}
					}
				} else {
					ViewToClickToExpand.CardElementUI cardElementUI = viewToClickToExpand
							.getCardElementUIToClick();
					if (cardElementUI != null) {
						switch (cardElementUI) {
						case CARD:
							viewToClick = this;
							break;
						case HEADER:
							viewToClick = getInternalHeaderLayout();
							break;
						case THUMBNAIL:
							viewToClick = getInternalThumbnailLayout();
							break;
						case MAIN_CONTENT:
							viewToClick = getInternalContentLayout();
							break;
						}
						if (viewToClick != null) {
							if (viewToClickToExpand.isUseLongClick()) {
								viewToClick
										.setOnLongClickListener(new TitleViewOnLongClickListener(
												titleViewOnClickListener));
							} else {
								viewToClick.setOnClickListener(titleViewOnClickListener);
							}
						}
					}
				}

				if (isExpanded()) {
					// Make layout visible and button selected
					mInternalExpandLayout.setVisibility(View.VISIBLE);
					if (viewToClick != null) {
						if (viewToClickToExpand.isViewToSelect())
							viewToClick.setSelected(true);
					}
				} else {
					// Make layout hidden and button not selected
					mInternalExpandLayout.setVisibility(View.GONE);
					if (viewToClick != null) {
						if (viewToClickToExpand.isViewToSelect())
							viewToClick.setSelected(false);
					}
				}
			}

		}
	}

	/**
	 * Setup Expand View
	 */
	protected void setupExpandView() {
		if (mInternalExpandLayout != null && mCardExpand != null) {

			// Check if view can be recycled
			// It can happen in a listView, and improves performances
			if (!isRecycle() || isForceReplaceInnerLayout()) {

				if (isForceReplaceInnerLayout() && mInternalExpandLayout != null
						&& mInternalExpandInnerView != null)
					((ViewGroup) mInternalExpandLayout).removeView(mInternalExpandInnerView);

				mInternalExpandInnerView = mCardExpand.getInnerView(getContext(),
						(ViewGroup) mInternalExpandLayout);
			} else {
				// View can be recycled.
				// Only setup Inner Elements
				if (mCardExpand.getInnerLayout() > -1)
					mCardExpand.setupInnerViewElements((ViewGroup) mInternalExpandLayout,
							mInternalExpandInnerView);
			}

			ViewGroup.LayoutParams layoutParams = mInternalExpandLayout.getLayoutParams();
			layoutParams.height = LayoutParams.WRAP_CONTENT;
			mInternalExpandLayout.setLayoutParams(layoutParams);
		}
	}

	public void doToggleExpand() {

		if (mInternalExpandLayout != null) {
			ExpandContainerHelper helper = new ExpandContainerHelper(mInternalExpandLayout, mCard,
					false);

			boolean isVisible = mInternalExpandLayout.getVisibility() == View.VISIBLE;
			if (isVisible) {
				ExpandCollapseHelper.animateCollapsing(helper);
			} else {
				ExpandCollapseHelper.animateExpanding(helper);
			}
		}
	}

	public void doExpand() {

		if (mInternalExpandLayout != null) {
			ExpandContainerHelper helper = new ExpandContainerHelper(mInternalExpandLayout, mCard,
					false);

			boolean isVisible = mInternalExpandLayout.getVisibility() == View.VISIBLE;
			if (!isVisible) {
				ExpandCollapseHelper.animateExpanding(helper);
			}
		}
	}

	public void doCollapse() {

		if (mInternalExpandLayout != null) {
			ExpandContainerHelper helper = new ExpandContainerHelper(mInternalExpandLayout, mCard,
					false);

			boolean isVisible = mInternalExpandLayout.getVisibility() == View.VISIBLE;
			if (isVisible) {
				ExpandCollapseHelper.animateCollapsing(helper);
			}
		}
	}

	/**
	 * Listener to expand/collapse hidden Expand Layout It starts animation
	 */
	protected class TitleViewOnClickListener implements View.OnClickListener {

		ExpandContainerHelper mExpandContainerHelper;

		private TitleViewOnClickListener(View contentParent, Card card) {
			this(contentParent, card, true);
		}

		private TitleViewOnClickListener(View contentParent, Card card, boolean viewToSelect) {
			mExpandContainerHelper = new ExpandContainerHelper(contentParent, card, viewToSelect);
		}

		@Override
		public void onClick(View view) {
			boolean isVisible = mExpandContainerHelper.contentParent.getVisibility() == View.VISIBLE;
			if (isVisible) {
				ExpandCollapseHelper.animateCollapsing(mExpandContainerHelper);
				if (mExpandContainerHelper.viewToSelect)
					view.setSelected(false);
			} else {
				ExpandCollapseHelper.animateExpanding(mExpandContainerHelper);
				if (mExpandContainerHelper.viewToSelect)
					view.setSelected(true);
			}
		}
	}

	/**
	 * Listener to expand/collapse hidden Expand Layout It starts animation
	 */
	protected class TitleViewOnLongClickListener implements OnLongClickListener {

		TitleViewOnClickListener mOnClickListener;

		private TitleViewOnLongClickListener(TitleViewOnClickListener onClickListener) {
			mOnClickListener = onClickListener;
		}

		@Override
		public boolean onLongClick(View view) {
			if (mOnClickListener != null) {
				mOnClickListener.onClick(view);
				return true;
			}
			return false;
		}
	}

	private class ExpandContainerHelper {

		private View contentParent;
		private Card card;
		private boolean viewToSelect = true;

		private ExpandContainerHelper(View contentParent, Card card, boolean viewToSelect) {
			this.contentParent = contentParent;
			this.card = card;
			this.viewToSelect = viewToSelect;
		}

		public CardView getCardView() {
			return (CardView) card.getCardView();
		}
	}

	@Override
	protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld) {
		super.onSizeChanged(xNew, yNew, xOld, yOld);
	}

	private static class ExpandCollapseHelper {

		/**
		 * Expanding animator.
		 */
		private static void animateExpanding(final ExpandContainerHelper helper) {

			// Callback
			if (helper.card.getOnExpandAnimatorStartListener() != null)
				helper.card.getOnExpandAnimatorStartListener().onExpandStart(helper.card);

			if (helper.getCardView().getOnExpandListAnimatorListener() != null) {
				// List Animator
				helper.getCardView().getOnExpandListAnimatorListener()
						.onExpandStart(helper.getCardView(), helper.contentParent);
			} else {
				// Std animator
				helper.contentParent.setVisibility(View.VISIBLE);
				if (helper.getCardView().mExpandAnimator != null) {
					helper.getCardView().mExpandAnimator.addListener(new AnimatorListenerAdapter() {
						@Override
						public void onAnimationEnd(Animator animation) {
							helper.card.setExpanded(true);
							// Callback
							if (helper.card.getOnExpandAnimatorEndListener() != null)
								helper.card.getOnExpandAnimatorEndListener().onExpandEnd(
										helper.card);
						}
					});
					helper.getCardView().mExpandAnimator.start();
				} else {
					if (helper.card.getOnExpandAnimatorEndListener() != null)
						helper.card.getOnExpandAnimatorEndListener().onExpandEnd(helper.card);
					Log.w(TAG, "Does the card have the ViewToClickToExpand?");
				}
			}
		}

		/**
		 * Collapse animator
		 */
		private static void animateCollapsing(final ExpandContainerHelper helper) {

			// Callback
			if (helper.card.getOnCollapseAnimatorStartListener() != null)
				helper.card.getOnCollapseAnimatorStartListener().onCollapseStart(helper.card);

			if (helper.getCardView().getOnExpandListAnimatorListener() != null) {
				// There is a List Animator.
				helper.getCardView().getOnExpandListAnimatorListener()
						.onCollapseStart(helper.getCardView(), helper.contentParent);
			} else {
				// Std animator
				int origHeight = helper.contentParent.getHeight();

				ValueAnimator animator = createSlideAnimator(helper.getCardView(), origHeight, 0);
				animator.addListener(new Animator.AnimatorListener() {
					@Override
					public void onAnimationStart(Animator animator) {
					}

					@Override
					public void onAnimationEnd(Animator animator) {
						helper.contentParent.setVisibility(View.GONE);
						helper.card.setExpanded(false);
						// Callback
						if (helper.card.getOnCollapseAnimatorEndListener() != null)
							helper.card.getOnCollapseAnimatorEndListener().onCollapseEnd(
									helper.card);
					}

					@Override
					public void onAnimationCancel(Animator animator) {
					}

					@Override
					public void onAnimationRepeat(Animator animator) {
					}
				});
				animator.start();
			}
		}

		/**
		 * Create the Slide Animator invoked when the expand/collapse button is
		 * clicked
		 */
		protected static ValueAnimator createSlideAnimator(final CardView cardView, int start,
				int end) {
			ValueAnimator animator = ValueAnimator.ofInt(start, end);

			animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
				@Override
				public void onAnimationUpdate(ValueAnimator valueAnimator) {
					int value = (Integer) valueAnimator.getAnimatedValue();

					ViewGroup.LayoutParams layoutParams = cardView.mInternalExpandLayout
							.getLayoutParams();
					layoutParams.height = value;
					cardView.mInternalExpandLayout.setLayoutParams(layoutParams);
				}
			});
			return animator;
		}

	}

	// -------------------------------------------------------------
	// OnExpandListAnimator Interface and Listener
	// -------------------------------------------------------------

	/**
	 * Returns the listener invoked when expand/collpase animation starts It is
	 * used internally
	 *
	 * @return listener
	 */
	public OnExpandListAnimatorListener getOnExpandListAnimatorListener() {
		return mOnExpandListAnimatorListener;
	}

	/**
	 * Sets the listener invoked when expand/collapse animation starts It is
	 * used internally. Don't override it.
	 *
	 * @param onExpandListAnimatorListener
	 *            listener
	 */
	public void setOnExpandListAnimatorListener(
			OnExpandListAnimatorListener onExpandListAnimatorListener) {
		this.mOnExpandListAnimatorListener = onExpandListAnimatorListener;
	}

	// -------------------------------------------------------------
	// Bitmap export
	// -------------------------------------------------------------

	/**
	 * Create a {@link android.graphics.Bitmap} from CardView
	 * 
	 * @return
	 */
	public Bitmap createBitmap() {

		if (getWidth() <= 0 && getHeight() <= 0) {
			int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
			measure(spec, spec);
			layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
		}

		Bitmap b = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
		Canvas c = new Canvas(b);
		draw(c);
		return b;
	}

	// -------------------------------------------------------------
	// Getter and Setter
	// -------------------------------------------------------------

	/**
	 * Returns the view used by Expand Layout
	 *
	 * @return {@link View} used by Expand Layout
	 */
	public View getInternalExpandLayout() {
		return mInternalExpandLayout;
	}

	/**
	 * FIXME
	 * 
	 * @return
	 */
	public View getInternalContentLayout() {
		return mInternalContentLayout;
	}

	/**
	 * FIXME
	 * 
	 * @return
	 */
	public View getInternalInnerView() {
		return mInternalInnerView;
	}

	/**
	 * Indicates if the card is expanded or collapsed
	 *
	 * @return <code>true</code> if the card is expanded
	 */
	public boolean isExpanded() {
		if (mCard != null) {
			return mCard.isExpanded();
		} else
			return false;
	}

	/**
	 * Sets the card as expanded or collapsed
	 *
	 * @param expanded
	 *            <code>true</code> if the card is expanded
	 */
	public void setExpanded(boolean expanded) {
		if (mCard != null) {
			mCard.setExpanded(expanded);
		}
	}

	/**
	 * Retrieves the InternalMainCardGlobalLayout. Background style is applied
	 * here.
	 *
	 * @return
	 */
	public View getInternalMainCardLayout() {
		return mInternalMainCardLayout;
	}

	/**
	 * Changes dynamically the drawable resource to override the style of
	 * MainLayout.
	 *
	 * @param drawableResourceId
	 *            drawable resource Id
	 */
	@Override
	public void changeBackgroundResourceId(int drawableResourceId) {
		if (drawableResourceId != 0) {
			if (mInternalMainCardLayout != null) {
				mInternalMainCardLayout.setBackgroundResource(drawableResourceId);
			}
		}
	}

	/**
	 * Changes dynamically the drawable resource to override the style of
	 * MainLayout.
	 *
	 * @param drawableResource
	 *            drawable resource
	 */
	@Override
	public void changeBackgroundResource(Drawable drawableResource) {
		if (drawableResource != null) {
			if (mInternalMainCardLayout != null) {
				mHelperImpl.setBackground(mInternalMainCardLayout, drawableResource);
			}
		}
	}

	@Override
	public void changeBackgroundColorResourceId(int colorResourceId) {
		// TODO : do nothing for now
	}
}
